package com.baidu.calculation_test

import android.os.Bundle
import android.widget.Button
import android.widget.ImageButton
import android.widget.TextView
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import android.content.Intent
import androidx.lifecycle.lifecycleScope

import com.baidu.calculation_test.ui.HistoryDialogFragment
import com.baidu.calculation_test.data.AppDatabase
import com.baidu.calculation_test.data.HistoryEntry
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlin.math.pow

class MainActivity : AppCompatActivity() {
    private lateinit var tvDisplay: TextView
    private lateinit var tvPreview: TextView

    private val currentInput = StringBuilder()
    private var firstOperand: Double? = null
    private var pendingOperator: Char? = null
    private var lastResult: Double? = null
    private var isErrorState: Boolean = false
    private var justEvaluated: Boolean = false

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContentView(R.layout.activity_main)
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
            val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
            insets
        }

        tvDisplay = findViewById(R.id.tvDisplay)
        tvPreview = findViewById(R.id.tvPreview)

        // Digits
        setDigitListener(R.id.btn0, '0')
        setDigitListener(R.id.btn1, '1')
        setDigitListener(R.id.btn2, '2')
        setDigitListener(R.id.btn3, '3')
        setDigitListener(R.id.btn4, '4')
        setDigitListener(R.id.btn5, '5')
        setDigitListener(R.id.btn6, '6')
        setDigitListener(R.id.btn7, '7')
        setDigitListener(R.id.btn8, '8')
        setDigitListener(R.id.btn9, '9')

        // Dot & Sign & Percent
        findViewById<Button>(R.id.btnDot).setOnClickListener { appendDot() }
        findViewById<Button>(R.id.btnSign).setOnClickListener { toggleSign() }
        findViewById<Button>(R.id.btnPercent).setOnClickListener { applyPercent() }

        // Operators
        findViewById<Button>(R.id.btnAdd).setOnClickListener { setOperator('+') }
        findViewById<Button>(R.id.btnSub).setOnClickListener { setOperator('-') }
        findViewById<Button>(R.id.btnMul).setOnClickListener { setOperator('×') }
        findViewById<Button>(R.id.btnDiv).setOnClickListener { setOperator('÷') }
        // Scientific (landscape)
        findViewById<Button?>(R.id.btnSin)?.setOnClickListener { applyUnaryFunction("sin") }
        findViewById<Button?>(R.id.btnCos)?.setOnClickListener { applyUnaryFunction("cos") }
        findViewById<Button?>(R.id.btnTan)?.setOnClickListener { applyUnaryFunction("tan") }
        findViewById<Button?>(R.id.btnPow)?.setOnClickListener { setOperator('^') }

        // Equals
        findViewById<Button>(R.id.btnEquals).setOnClickListener { onEquals() }

        // Clear and Backspace
        findViewById<Button>(R.id.btnClear).setOnClickListener { clearAll() }
        findViewById<Button>(R.id.btnBack).setOnClickListener { backspace() }

        // History icon -> dialog
        findViewById<ImageButton>(R.id.btnHistory).setOnClickListener {
            HistoryDialogFragment().show(supportFragmentManager, "history_dialog")
        }

        if (savedInstanceState != null) {
            restoreState(savedInstanceState)
        } else {
            updateDisplay("0")
        }
    }

    private fun setDigitListener(buttonId: Int, digit: Char) {
        findViewById<Button>(buttonId).setOnClickListener {
            if (isErrorState) clearAll()
            startNewInputIfJustEvaluated()
            if (currentInput.isEmpty()) {
                // First digit
                if (digit != '0') {
                    currentInput.append(digit)
                } else {
                    currentInput.append('0')
                }
            } else if (currentInput.length == 1 && currentInput[0] == '0') {
                // Replace leading zero with non-zero; keep single zero if digit is zero
                if (digit != '0') currentInput.setCharAt(0, digit)
            } else {
                currentInput.append(digit)
            }
            updateDisplay(currentInput.toString())
        }
    }

    private fun appendDot() {
        if (isErrorState) clearAll()
        startNewInputIfJustEvaluated()
        if (currentInput.isEmpty()) {
            currentInput.append("0.")
        } else if (!currentInput.contains('.')) {
            currentInput.append('.')
        }
        updateDisplay(currentInput.toString())
    }

    private fun setOperator(operator: Char) {
        if (isErrorState) return
        val value = currentInput.toString().toDoubleOrNull()
        if (value != null) {
            if (firstOperand == null) {
                firstOperand = value
            } else if (pendingOperator != null) {
                val result = calculate(firstOperand!!, value, pendingOperator!!)
                if (result == null) {
                    showError()
                    return
                }
                firstOperand = result
            }
        } else if (firstOperand == null && lastResult != null) {
            // Use last result as first operand if no current input
            firstOperand = lastResult
        }
        currentInput.clear()
        pendingOperator = operator
        val displayText = firstOperand?.let { formatNumber(it) } ?: "0"
        updateDisplay(displayText)
    }

    private fun onEquals() {
        if (isErrorState) return
        val secondOperand = currentInput.toString().toDoubleOrNull()
        if (firstOperand != null && pendingOperator != null && secondOperand != null) {
            val result = calculate(firstOperand!!, secondOperand, pendingOperator!!)
            if (result == null) {
                showError()
                return
            }
            lastResult = result
            updateDisplay(formatNumber(result))
            // Prepare for chaining: result becomes first operand, clear operator until user selects new one
            firstOperand = result
            pendingOperator = null
            currentInput.clear()
            justEvaluated = true

            // Save history (expression preview + result)
            val expression = tvPreview.text.toString().ifEmpty { formatNumber(result) }
            val resText = formatNumber(result)
            lifecycleScope.launch(Dispatchers.IO) {
                AppDatabase.get(this@MainActivity).historyDao().insert(
                    HistoryEntry(
                        expression = expression,
                        result = resText,
                        timestamp = System.currentTimeMillis()
                    )
                )
            }
        }
    }

    private fun calculate(a: Double, b: Double, op: Char): Double? {
        return when (op) {
            '+' -> a + b
            '-' -> a - b
            '×' -> a * b
            '÷' -> if (b == 0.0) null else a / b
            '^' -> a.pow(b)
            else -> b
        }
    }

    private fun applyUnaryFunction(name: String) {
        if (isErrorState) clearAll()
        val value = currentInput.toString().toDoubleOrNull()
        val input = value ?: firstOperand
        if (input != null) {
            val radians = Math.toRadians(input)
            val result = when (name) {
                "sin" -> kotlin.math.sin(radians)
                "cos" -> kotlin.math.cos(radians)
                "tan" -> kotlin.math.tan(radians)
                else -> input
            }
            currentInput.clear()
            pendingOperator = null
            firstOperand = result
            updateDisplay(formatNumber(result))
            justEvaluated = true
        }
    }

    private fun clearAll() {
        currentInput.clear()
        firstOperand = null
        pendingOperator = null
        lastResult = null
        isErrorState = false
        justEvaluated = false
        updateDisplay("0")
    }

    private fun backspace() {
        if (isErrorState) {
            clearAll()
            return
        }
        if (currentInput.isNotEmpty()) {
            currentInput.deleteCharAt(currentInput.length - 1)
            if (currentInput.isEmpty()) {
                updateDisplay("0")
            } else {
                updateDisplay(currentInput.toString())
            }
        }
    }

    private fun updateDisplay(text: String) {
        tvDisplay.text = text
        // Update preview formula
        val left = firstOperand?.let { formatNumber(it) }
        val op = pendingOperator?.toString() ?: ""
        val right = if (currentInput.isNotEmpty()) currentInput.toString() else ""
        val preview = buildString {
            if (!left.isNullOrEmpty()) append(left)
            if (op.isNotEmpty()) append(" ").append(op)
            if (right.isNotEmpty()) append(" ").append(right)
        }
        tvPreview.text = preview
    }

    private fun toggleSign() {
        if (isErrorState) clearAll()
        startNewInputIfJustEvaluated()
        if (currentInput.isEmpty()) {
            currentInput.append("0")
        }
        if (currentInput.startsWith("-")) {
            currentInput.deleteCharAt(0)
        } else if (!(currentInput.length == 1 && currentInput[0] == '0')) {
            currentInput.insert(0, '-')
        }
        updateDisplay(currentInput.toString())
    }

    private fun applyPercent() {
        if (isErrorState) clearAll()
        startNewInputIfJustEvaluated()
        val value = currentInput.toString().toDoubleOrNull()
        if (value != null) {
            val percent = value / 100.0
            currentInput.clear()
            currentInput.append(formatNumber(percent))
            updateDisplay(currentInput.toString())
        }
    }

    private fun startNewInputIfJustEvaluated() {
        if (justEvaluated && pendingOperator == null) {
            currentInput.clear()
            firstOperand = null
            lastResult = null
            tvPreview.text = ""
            justEvaluated = false
        }
    }


    private fun formatNumber(value: Double): String {
        if (value.isNaN() || value.isInfinite()) return "Error"
        val longValue = value.toLong()
        if (value == longValue.toDouble()) return longValue.toString()
        // Use DecimalFormat for controlled precision
        val df = java.text.DecimalFormat()
        df.maximumFractionDigits = 8
        df.minimumFractionDigits = 0
        df.isGroupingUsed = false
        val s = df.format(value)
        return if (s.length > 16) value.toString() else s
    }

    private fun showError() {
        isErrorState = true
        updateDisplay("Error")
        currentInput.clear()
        firstOperand = null
        pendingOperator = null
        lastResult = null
    }

    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        outState.putString("currentInput", currentInput.toString())
        outState.putSerializable("firstOperand", firstOperand)
        outState.putSerializable("pendingOperator", pendingOperator)
        outState.putSerializable("lastResult", lastResult)
        outState.putBoolean("isErrorState", isErrorState)
        outState.putBoolean("justEvaluated", justEvaluated)
        outState.putString("preview", tvPreview.text.toString())
    }

    private fun restoreState(state: Bundle) {
        currentInput.clear()
        currentInput.append(state.getString("currentInput", ""))
        firstOperand = state.getSerializable("firstOperand") as? Double
        pendingOperator = state.getSerializable("pendingOperator") as? Char
        lastResult = state.getSerializable("lastResult") as? Double
        isErrorState = state.getBoolean("isErrorState", false)
        justEvaluated = state.getBoolean("justEvaluated", false)
        val displayText = if (currentInput.isNotEmpty()) currentInput.toString() else firstOperand?.let { formatNumber(it) } ?: "0"
        updateDisplay(displayText)
        tvPreview.text = state.getString("preview", tvPreview.text.toString())
    }
}